
`include "common_header.verilog"

//  *************************************************************************
//  File : rx_axi4s_ffmap.v
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2017 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Geoffrey Chacon
//  info@morethanip.com
//  *************************************************************************
//  Description : Receive FIFO to AXI4 interface converter.
//                Wrapper to map the rx status/preamble/timestamp values
//                into tuser and latch as appropriate. 
//  Version     : $Id: rx_axi4s_ffmap.v,v 1.2 2018/03/06 09:36:44 dk Exp $
//  *************************************************************************
//
// Note: Set RXFIFO_RDY_DELAY to the actual FIFO ff_rx_rdy -> ~ff_rx_dval latency.
//       When no FIFO is present, then set it to zero. However, this will cause 
//       that a tready deassertion can corrupt a frame.
//       A small DFF can be instantiated by setting RXFIFO_RDY_DELAY > 0, but without an RX
//       FIFO, this can only work if tready can be guaranteed to not deassert frequently


module rx_axi4s_ffmap #(
        parameter FLOP_INPUTS     = 0,                   // Add input registers. Not really needed for this interface
        parameter FLOP_OUTPUTS    = 1,                   // Add output registers
        parameter RXFIFO_RDY_DELAY = 2,                  // Number of extra words after ff_rx_rdy deasserts
        parameter DATA_WIDTH      = 256,                 // TDATA width in bits (multiples of 8)
        parameter DATA_MOD        = Clogb2(DATA_WIDTH/8),// Set to log2(DATA_WIDTH/8)
        parameter KEEP_WIDTH      = DATA_WIDTH/8,        // TKEEP width. 1 bit per byte
        parameter USER_WIDTH      = (1+64+25),           // TUSER width, application specific - ING_USER_WIDTH
        parameter TS_WIDTH        = 64,                  // Data width of Timestamp
        parameter RX_FF_STAT_WIDTH = 25                  // Status Data width
) (
        input                    reset,       // Active High Reset
        input                    clk,         // Application clock
        input                    cfg_write64, // Configures the FIFO to use only the lower 64-bit of the data bus. Only for FIFOs of 128-bits. Drive to 0 if unused
        input                    sw_reset,    // FIFO sync reset (clk)

        /// AXI4 interface
        output                   tvalid,        // tVALID -> valid transaction
        output                   tlast,         // tVALID -> last transaction for a frame
        output  [KEEP_WIDTH-1:0] tkeep,         // tKEEP  -> valid byte qualifier, 1 bit per byte in tdata
        output  [DATA_WIDTH-1:0] tdata,         // tDATA  -> frame data, left aligned
        output  [USER_WIDTH-1:0] tuser,         // tUSER  -> sideband data. 
        //output                   terr,          // tERR   -> custom signal. OR to the error bit in tuser on the top level     ==> is TUSER[0]
        input                    tready,        // tREADY -> interface backpressure

        /// FIFO interface
        
        `ifdef MTIP_M128_USER_PREAMBLE_ENA
          `ifndef MTIP_M128_SHORT_PREAM
        input   [55:0]                  ff_rx_preamble, //  Receive Preamble
          `ifdef MTIP_M128_PARITY_PROT
        input                           ff_rx_preamble_par,// Frame Preamble parity
          `endif
          `endif
        `endif

        `ifdef MTIP_M128_PARITY_PROT
        input   [(DATA_WIDTH/64)-1:0]   ff_rx_data_par, //  Data Parity Out     // ING_PAR_WIDTH
        input                           ff_rx_ctrl_par, //  Control Parity Out
        input                           ff_rx_stat_par, //  Status Parity
        `endif

        input                           ff_rx_dval,       //  Write Enable from application
        input                           ff_rx_sop,        //  Start of Packet
        input                           ff_rx_eop,        //  End of Packet
        input   [DATA_MOD-1:0]          ff_rx_emod,       //  Last Word Modulo
        input                           ff_rx_err,      //  Errored Packet Indication
        input   [RX_FF_STAT_WIDTH-1:0]  ff_rx_err_stat, //  Errored Packet Status Word
        input   [TS_WIDTH-1:0]          ff_rx_ts,       //  Timestamp of the frame (valid with SOP)
        input   [DATA_WIDTH-1:0]        ff_rx_data,       //  Data Out
        output                          ff_rx_rdy         //  almost full, fifo ready

);

// ----------------------------------------------------------------------------------------------------
// Local functions
// ----------------------------------------------------------------------------------------------------
// Constant log2
function integer Clogb2(input integer idepth);
begin : f_clogb2
        integer depth, i;
        depth = idepth;
        if (depth <= 1)
        begin
                Clogb2 = 1;
        end
        else
        begin
                depth = depth - 1;

                for (i = 0; depth > 0; i = i + 1)
                begin
                        depth = depth >> 1;
                end

                Clogb2 = i;
        end
end
endfunction

// local 

wire                        ts_vld;     //  Timestamp valid with SOP.
wire    [TS_WIDTH-1:0]      ff_rx_ts_l; //  Timestamp latched with SOP.
wire    [TS_WIDTH-1:0]      ff_rx_ts_stable; //  timestamp mux to have it stable during eop

wire    [USER_WIDTH-1:0]    ff_rx_user;
wire    [USER_WIDTH-1:0]    tuser_int;
wire                        terr_int;

// timestamp is defined to be valid during SOP only. 
// changes at eop (then showing shared status) but must keep tuser value stable.
// ----------

assign ts_vld = ff_rx_dval & ff_rx_sop;

mtip_dffvec_ena #(TS_WIDTH) U_TSLATCH (
        
        .reset  (reset),
        .clk    (clk),
        .ena    (ts_vld),
        .i      (ff_rx_ts),
        .o      (ff_rx_ts_l));

assign ff_rx_ts_stable = (ts_vld == 1'b 1) ? ff_rx_ts :   // during SOP direct through
                                             ff_rx_ts_l;


// Sideband data from FIFO to AXI input
// ----------

// tuser is flexible in general, but right now it is coded as follows:
// 0                                    ff_rx_err
// 1          +:TS_WIDTH                ff_rx_ts
// 1+TS_WIDTH +:RX_FF_STAT_WIDTH        ff_rx_err_stat
// 1+TS_WIDTH+RX_FF_STAT_WIDTH          ff_rx_ctrl_par
//                                      ff_rx_stat_par
//                                      ff_rx_data_par
//                                      ff_rx_preamble_par
//                                      ff_rx_preamble

assign ff_rx_user = {
`ifdef MTIP_M128_USER_PREAMBLE_ENA
        `ifndef MTIP_M128_SHORT_PREAM
                     ff_rx_preamble,     // Receive Preamble
        `ifdef MTIP_M128_PARITY_PROT
                     ff_rx_preamble_par, // Frame Preamble parity
        `endif
        `endif
`endif
`ifdef MTIP_M128_PARITY_PROT
                     ff_rx_data_par,     // Data Parity Out
                     ff_rx_stat_par,     // Status Parity Out (ff_rx_err_stat+ff_rx_err)
                     ff_rx_ctrl_par,     // Control Parity Out (sop+eop+mod)
`endif
                     ff_rx_err_stat,
                     ff_rx_ts_stable,   // ff_rx_ts,
                     ff_rx_err
                    };

// AXI output
// ----------

// OR the error from the AXI adapter and fix parity if needed
`ifdef MTIP_M128_PARITY_PROT

assign tuser[USER_WIDTH-1 : 3+TS_WIDTH+RX_FF_STAT_WIDTH]        =  tuser_int[USER_WIDTH-1 : 3+TS_WIDTH+RX_FF_STAT_WIDTH];               // data_par + preamble_par + preamble
assign tuser[2+TS_WIDTH+RX_FF_STAT_WIDTH]                       =  tuser_int[2+TS_WIDTH+RX_FF_STAT_WIDTH] ^ ((tuser_int[0] ^ terr_int) & terr_int);  // stat_par 
assign tuser[1+TS_WIDTH+RX_FF_STAT_WIDTH]                       =  tuser_int[1+TS_WIDTH+RX_FF_STAT_WIDTH];                              // ctrl_par 
assign tuser[1 +: (TS_WIDTH+RX_FF_STAT_WIDTH)]                  =  tuser_int[1 +: (TS_WIDTH+RX_FF_STAT_WIDTH)];
assign tuser[0]                                                 =  tuser_int[0] | terr_int;

`else
assign tuser = {tuser_int[USER_WIDTH-1:1],
                tuser_int[0] | terr_int
               };

`endif

//assign terr = terr_int;       // not needed, is TUSER[0]



// ------------------------------------------
// Generic AXI4S receive Interface Controller
// ------------------------------------------

rx_axi4s_ff #(
        .FLOP_INPUTS     (FLOP_INPUTS),
        .FLOP_OUTPUTS    (FLOP_OUTPUTS),
`ifdef MTIP_M128_RXNOFIFO
        // When no fifo is used, set the delay to zero to corrupt frames if axi_rtready asserts
        .RXFIFO_RDY_DELAY(RXFIFO_RDY_DELAY),
`else
        // Our FIFO has a one cycle delay (must be set to 2 if FF_OUT_REGISTERED is set to 1 in the FIFO)
        .RXFIFO_RDY_DELAY(RXFIFO_RDY_DELAY),
`endif
        .DATA_WIDTH      (DATA_WIDTH),
        .DATA_MOD        (DATA_MOD),
        .KEEP_WIDTH      (KEEP_WIDTH),
        .USER_WIDTH      (USER_WIDTH)

) U_RX_AXI4S_FF ( 

        .reset        (reset      ),
        .clk          (clk        ),
        .cfg_write64  (cfg_write64),
        .sw_reset     (sw_reset   ),

        /// AXI interface
        .tvalid       (tvalid     ),
        .tlast        (tlast      ),
        .tkeep        (tkeep      ),
        .tdata        (tdata      ),
        .tuser        (tuser_int  ),            // local
        .terr         (terr_int   ),            // local
        .tready       (tready     ),

        /// FIFO interface
        .ff_rx_dval   (ff_rx_dval ),
        .ff_rx_sop    (ff_rx_sop  ),
        .ff_rx_eop    (ff_rx_eop  ),
        .ff_rx_emod   (ff_rx_emod ),
        .ff_rx_user   (ff_rx_user ),
        .ff_rx_data   (ff_rx_data ),
        .ff_rx_rdy    (ff_rx_rdy  )
);


endmodule
